// #include <open62541/plugin/log_stdout.h>
// #include <open62541/server.h>
// #include <open62541/server_config_default.h>
#include <open62541.h>
//#include "open62541.h"
#include <signal.h>
#include <stdlib.h>
#include "common.h"
// #include <open62541/client_config_default.h>
// #include <open62541/client_highlevel.h>
// #include <open62541/plugin/log_stdout.h>
// #include <open62541/plugin/securitypolicy.h>

#define MIN_ARGS 3

int main(int argc, char **argv) {

    /*
    在可执行的程序后面加上 0和1 代表想要执行的是读还是写操作
    客户端的证书和钥匙是每次运行时自行生成的，没有去导入现有的文件
    OPC UA通信过程支持非对称加密，和用户身份认证
    */
    //const char *endpointUrl = argv[1];

    /* 加载client的证书和私匙 */
    // UA_ByteString certificate = loadFile("./client_cert.der");
    // UA_ByteString privateKey  = loadFile("./client_key.der");
    //完成用户账户和密码信息的导入工作
    /* 加载server的证书和私匙 */
    UA_ByteString certificate = UA_BYTESTRING_NULL;
    UA_ByteString privateKey  = UA_BYTESTRING_NULL;
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                "Trying to create a certificate.");
    UA_String subject[3] = {UA_STRING_STATIC("C=DE"),
                        UA_STRING_STATIC("O=SampleOrganization"),
                        UA_STRING_STATIC("CN=Open62541Server@localhost")};
    UA_UInt32 lenSubject = 3;
    UA_String subjectAltName[2]= {
        UA_STRING_STATIC("DNS:localhost"),
        UA_STRING_STATIC("URI:urn:open62541.client.application")
    };
    UA_UInt32 lenSubjectAltName = 2;
    UA_StatusCode statusCertGen =
        UA_CreateCertificate(UA_Log_Stdout,
                              subject, lenSubject,
                              subjectAltName, lenSubjectAltName,
                              0, UA_CERTIFICATEFORMAT_DER,
                              &privateKey, &certificate);

    if(statusCertGen != UA_STATUSCODE_GOOD) {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
            "Generating Certificate failed: %s",
            UA_StatusCode_name(statusCertGen));
        return EXIT_FAILURE;
    }

    /* 加载trustList. revocationList目前还不支持 */
    //暂时采用写死的方式去进行控制
    size_t trustListSize = 0;
    // if(argc > MIN_ARGS)
    //     trustListSize = (size_t)argc-MIN_ARGS;
    //UA_STACKARRAY(UA_ByteString, trustList, trustListSize);
    UA_ByteString* trustList = NULL;
    //trustList[0] = loadFile("./server_cert.der");
    // for(size_t trustListCount = 0; trustListCount < trustListSize; trustListCount++)
    //     trustList[trustListCount] = loadFile(argv[trustListCount+4]);

    UA_ByteString *revocationList = NULL;
    size_t revocationListSize = 0;

    UA_Client *client = UA_Client_new();
    UA_ClientConfig *cc = UA_Client_getConfig(client);

    cc->securityMode = UA_MESSAGESECURITYMODE_SIGNANDENCRYPT;
    UA_ClientConfig_setDefaultEncryption(cc, certificate, privateKey,
                                         trustList, trustListSize,
                                         revocationList, revocationListSize);    
    UA_ByteString_clear(&certificate);
    UA_ByteString_clear(&privateKey);
    for(size_t deleteCount = 0; deleteCount < trustListSize; deleteCount++) {
        UA_ByteString_clear(&trustList[deleteCount]);
    }
    UA_StatusCode retval = UA_Client_connectUsername(client, "opc.tcp://localhost:4000", "test2", "111222");
    if(retval != UA_STATUSCODE_GOOD) {
        UA_Client_delete(client);
        //
        return (int)retval;
    }
    /* Read the value attribute of the node. UA_Client_readValueAttribute is a
    * wrapper for the raw read service available as UA_Client_Service_read. */
    UA_Variant value; /* Variants can hold scalar values and arrays of any type */
    UA_Variant_init(&value);
    /* NodeId of the variable holding the current time */
    int action = atoi(argv[1]);
    printf("action:%d\n",action);
    if(action == 0){
        //第一个参数1，是变量的命名空间，不会改变，后面的参数是变量名，
        //测试的时候改变这个参数即可
        const UA_NodeId nodeId = UA_NODEID_STRING(1, "array_test3");
        //const UA_NodeId nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
        // const UA_NodeId nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
        retval = UA_Client_readValueAttribute(client, nodeId, &value);
        if(retval == UA_STATUSCODE_GOOD) {
            //UA_Int16 raw_date = *(UA_Int16*) value.data;
            //UA_Byte raw_date = *(UA_Byte*) value.data;
            //UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "num is: %X\n",raw_date);

            UA_Int16  *reg_tab = (UA_Int16 *)value.data;
            for(size_t i = 0; i < value.arrayLength; ++i){
                UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "num is: %X\n",reg_tab[i]);
            }
            
        }else{
            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Fail to get data\n");
        }
    }else if(action == 1){
        UA_Int16  tab_reg[2]={0xFF34,0xFF78};
        UA_Variant_setArray(&value,tab_reg,2,&UA_TYPES[UA_TYPES_INT16]);
        UA_NodeId currentNodeId = UA_NODEID_STRING(1,"array_test3");
        retval = UA_Client_writeValueAttribute(client, currentNodeId, &value);
        if(retval == UA_STATUSCODE_GOOD) {
            //UA_Int16 raw_date = *(UA_Int16*) value.data;
            //UA_Byte raw_date = *(UA_Byte*) value.data;
            //UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "num is: %X\n",raw_date);
            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Successful to wrtie data\n");
        }else{
            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Fail to wrtie data\n");
        }        
    }

    // if(retval == UA_STATUSCODE_GOOD &&UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
    //     UA_DateTime raw_date = *(UA_DateTime *) value.data;
    //     UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);
    //     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "date is: %u-%u-%u %u:%u:%u.%03u\n",
    //     dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec);
    // }
    /* Clean up */
    /*
    删除的时候很容易出错误
    */
    //UA_Variant_clear(&value);
    UA_Client_delete(client); /* Disconnects the client internally */
    return EXIT_SUCCESS;
}

